Skip to content

Add GeoDjango support #308

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open

Add GeoDjango support #308

wants to merge 2 commits into from

Conversation

timgraham
Copy link
Collaborator

@timgraham timgraham commented May 24, 2025

No description provided.

@timgraham timgraham force-pushed the gis branch 5 times, most recently from c15694b to 3020173 Compare May 29, 2025 20:27
@timgraham timgraham force-pushed the gis branch 2 times, most recently from ec1c627 to 3f8b0fd Compare July 20, 2025 01:49
@timgraham timgraham force-pushed the gis branch 2 times, most recently from edbeeaa to de821e8 Compare July 26, 2025 20:46
@timgraham timgraham force-pushed the gis branch 7 times, most recently from dc8bb43 to a58fd36 Compare July 26, 2025 22:08
@timgraham timgraham force-pushed the gis branch 3 times, most recently from c50f38c to 50550c4 Compare July 31, 2025 13:41
@timgraham timgraham force-pushed the gis branch 5 times, most recently from e50cf56 to 7f51e94 Compare July 31, 2025 19:11
@@ -0,0 +1,59 @@
# Identical to test-python-atlas.yml except that gdal-bin is also installed.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding both .github/workflows/test-python-geo.yml and .github/workflows/test-python-atlas-geo.yml might be redundant. (Are there any notable differences in GEO support on Atlas?) When the Atlas build ran with transactions, it was perhaps slightly more useful.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no differences between Geospatial querying and indexing done on Atlas vs. what is done on local atlas.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant that test-python-geo.yml uses supercharge/mongodb-github-action (is this "standalone"?) and test-python-atlas-geo.yml uses the Atlas VM.

@timgraham timgraham marked this pull request as ready for review July 31, 2025 19:38
@timgraham timgraham requested review from aclark4life and Jibola July 31, 2025 20:17
Copy link
Contributor

@Jibola Jibola left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks good! Added a few questions, and would just like a test to confirm the 5 (of 6) data types can be inserted properly (even if un-queryable at the moment)

@@ -0,0 +1,59 @@
# Identical to test-python-atlas.yml except that gdal-bin is also installed.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are no differences between Geospatial querying and indexing done on Atlas vs. what is done on local atlas.


- MongoDB doesn't support any spatial reference system identifiers
(:attr:`BaseSpatialField.srid <django.contrib.gis.db.models.BaseSpatialField.srid>`)
besides 4326 (WGS84) .
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a link here?

Suggested change
besides 4326 (WGS84) .
besides `4326 (WGS84) <https://spatialreference.org/ref/epsg/4326/>` .

Comment on lines +12 to +16
#. Install the necessary :doc:`Geospatial libraries
<django:ref/contrib/gis/install/geolibs>` (GEOS and GDAL).
#. Add :mod:`django.contrib.gis` to :setting:`INSTALLED_APPS` in your settings.
This is so that the ``gis`` templates can be located -- if not done, then
features such as the geographic admin or KML sitemaps will not function properly.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reservation to having it be an extension for us django-mongodb-backend[geos] or something of that nature?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The geospatial libraries aren't Python packages.

Comment on lines +558 to +577
class GISTests(TestMixin, TransactionTestCase):
@isolate_apps("schema_")
def test_create_model(self):
"""
Spatial indexes for embedded GIS fields are created when the collections are
created.
"""
from django.contrib.gis.db.models import PointField # noqa: PLC0415

class Place(EmbeddedModel):
name = models.CharField(max_length=10)
location = PointField()

class Meta:
app_label = "schema_"

class Author(models.Model):
birthplace = EmbeddedModelField(Place)

class Meta:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have a test that confirms field storage and database schema? For MongoDB GeoJSON format still requires:
{ type: <GeoJSON Type>, ...} as the data format. I see in the Adapter does the work to ensure proper schema conversion, but I think it still helps to have a test.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like a query with pymongo.find()? Another thing to consider (if it assuages your concern) is that MongoDB raises an error (e.g. "can't extract geo keys") if a spatial index receives invalid data.

Comment on lines 7 to 10
def __init__(self, obj, geography=False):
"""
Initialize on the spatial object.
"""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a link to the MongoDB GeoJSON Object types in this docstring?
https://www.mongodb.com/docs/manual/reference/geojson/

@timgraham
Copy link
Collaborator Author

would just like a test to confirm the 5 (of 6) data types can be inserted properly (even if un-queryable at the moment)

7 out of 7 work (PointField, LineStringField, PolygonField, MultiPointField, MultiLineStringField, MultiPolygonField, GeometryCollectionField).

It's tested in Django's test suite. Surprisingly, I had to add some tests for a few of the fields to our Django fork:

Add tests for MultiPointField, MultiLineStringField, and GeometryCollectionField
commit 9dc4d37d7e8bb514922bd51c1a0940f5537e2c35
Author: Tim Graham <timograham@gmail.com>
Date:   Fri Jul 25 15:41:50 2025 -0400

    Add tests for MultiPointField, MultiLineStringField, and GeometryCollectionField
    
    These should be contributed upstream to Django.

diff --git a/tests/gis_tests/geoapp/models.py b/tests/gis_tests/geoapp/models.py
index 2c13c827c6..c7acb653d3 100644
--- a/tests/gis_tests/geoapp/models.py
+++ b/tests/gis_tests/geoapp/models.py
@@ -102,3 +102,15 @@ class ManyPointModel(NamedModel):
     point1 = models.PointField()
     point2 = models.PointField()
     point3 = models.PointField(srid=3857)
+
+
+class Points(models.Model):
+    geom = models.MultiPointField()
+
+
+class Lines(models.Model):
+    geom = models.MultiLineStringField()
+
+
+class GeometryCollections(models.Model):
+    geom = models.GeometryCollectionField()
diff --git a/tests/gis_tests/geoapp/tests.py b/tests/gis_tests/geoapp/tests.py
index f9abae129f..945d80a563 100644
--- a/tests/gis_tests/geoapp/tests.py
+++ b/tests/gis_tests/geoapp/tests.py
@@ -26,10 +26,13 @@ from .models import (
     City,
     Country,
     Feature,
+    GeometryCollections,
+    Lines,
     MinusOneSRID,
     MultiFields,
     NonConcreteModel,
     PennsylvaniaCity,
+    Points,
     State,
     ThreeDimensionalFeature,
     Track,
@@ -269,6 +272,47 @@ class GeoModelTest(TestCase):
             self.assertEqual(feature.geom.srid, g.srid)
 
 
+# TODO: contribute these tests added to the MongoDB fork upstream to Django.
+class SaveLoadTests(TestCase):
+    def test_multi_line_string_field(self):
+        geom = MultiLineString(
+            LineString((0, 0), (1, 1), (5, 5)),
+            LineString((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)),
+        )
+        obj = Lines.objects.create(geom=geom)
+        obj.refresh_from_db()
+        self.assertEqual(obj.geom.tuple, geom.tuple)
+
+    def test_multi_line_string_with_linear_ring(self):
+        # LinearRings are transformed to LineString
+        geom = MultiLineString(
+            LineString((0, 0), (1, 1), (5, 5)),
+            LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0)),
+        )
+        obj = Lines.objects.create(geom=geom)
+        obj.refresh_from_db()
+        self.assertEqual(obj.geom.tuple, geom.tuple)
+        self.assertEqual(obj.geom[0].tuple, geom[0].tuple)
+        self.assertEqual(obj.geom[1].__class__.__name__, "LineString")
+        self.assertEqual(obj.geom[1].tuple, geom[1].tuple)
+
+    def test_multi_point_field(self):
+        geom = MultiPoint(Point(1, 1), Point(0, 0))
+        obj = Points.objects.create(geom=geom)
+        obj.refresh_from_db()
+        self.assertEqual(obj.geom, geom)
+
+    def test_geometry_collection_field(self):
+        geom = GeometryCollection(
+            Point(2, 2),
+            LineString((0, 0), (2, 2)),
+            Polygon(LinearRing((0, 0), (0, 5), (5, 5), (5, 0), (0, 0))),
+        )
+        obj = GeometryCollections.objects.create(geom=geom)
+        obj.refresh_from_db()
+        self.assertEqual(obj.geom, geom)
+
+
 class GeoLookupTest(TestCase):
     fixtures = ["initial"]

@timgraham timgraham requested a review from Jibola August 7, 2025 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants